/*
 *  ifxvariable.cpp - Informix data type buffer classes implementation
 *
 *    Copyright (c) 2001-2006, Luxena Software Company. All rights reserved.
 *
 *  Purpose:
 *
 */

#include "ifxvariable.hpp"

#include <new>
#include <math.h>
#include <sqlhdr.h>
#include "ifxdefs.hpp"
#include "ifxerror.hpp"
#include "ifxconversion.hpp"


/////////////////////////////////////////////////////////////////////////////
// CInformixAbstractStringValue

CInformixAbstractStringValue::CInformixAbstractStringValue (int4 length)
{
	_length = length;
	_value = new char [length];
}

CInformixAbstractStringValue::CInformixAbstractStringValue (int4 length, char *value)
{
	_length = length - 1;
	_value = new char [length];
	memcpy(_value, value, length);
}

CInformixAbstractStringValue::~CInformixAbstractStringValue ()
{
	delete [] _value;
}

int4 CInformixAbstractStringValue::getLength ()
{
	return _length;
}

char * CInformixAbstractStringValue::getData ()
{
	return _value;
}

void CInformixAbstractStringValue::asString (char *value)
{
	memcpy(value, _value, _length);
}

CInformixAbstractStringValue::operator char * ()
{
	return _value;
}


/////////////////////////////////////////////////////////////////////////////
// CInformixAbstractDecimalValue

CInformixAbstractDecimalValue::CInformixAbstractDecimalValue (int4 length, IFetchSubject *fetchSubject)
{
	_length = length;
	_hasDouble = false;
	_hasBcd = false;
	fetchSubject->attach(this);
}

CInformixAbstractDecimalValue::CInformixAbstractDecimalValue (pFMTBcd value)
{
	_length = (value->iPrecision << 8) + value->iSignSpecialPlaces & 0x3f;
	bcd_to_decimal(value, &_value);
}

int4 CInformixAbstractDecimalValue::getLength ()
{
	return _length;
}

char * CInformixAbstractDecimalValue::getData ()
{
	return reinterpret_cast<char *>(&_value);
}

double CInformixAbstractDecimalValue::asDouble ()
{
	if (!_hasDouble) {
		_double = decimal_to_double(&_value);
		_hasDouble = true;
	}
	return _double;
}

FMTBcd& CInformixAbstractDecimalValue::asFMTBcd ()
{
	if (!_hasBcd) {
		decimal_to_bcd(&_value, &_bcd, _length >> 8, _length & 0xff);
		_hasBcd = true;
	}
	return _bcd;
}

void CInformixAbstractDecimalValue::update ()
{
	_hasDouble = false;
	_hasBcd = false;
}


/////////////////////////////////////////////////////////////////////////////
// CInformixDateTimeValue

CInformixDateTimeValue::CInformixDateTimeValue (int2 qualifier, IFetchSubject *fetchSubject)
{
	_value.dt_qual = qualifier;
	_isConverted = false;
	fetchSubject->attach(this);
}

CInformixDateTimeValue::CInformixDateTimeValue (INT32 value)
{
	time_to_dtime(value, &_value);
}

CInformixDateTimeValue::CInformixDateTimeValue (DFLOAT value)
{
	timestamp_to_dtime(value, &_value);
}

CInformixDateTimeValue::CInformixDateTimeValue (pCTIMESTAMP value)
{
	ctimestamp_to_dtime(value, &_value);
}

int2 CInformixDateTimeValue::getType ()
{
	return SQLDTIME;
}

int4 CInformixDateTimeValue::getLength ()
{
	return _value.dt_qual;
}

char * CInformixDateTimeValue::getData ()
{
	return reinterpret_cast<char *>(&_value);
}

CInformixDateTimeValue::operator CTIMESTAMP& ()
{
	if (!_isConverted) {
		dtime_to_ctimestamp(&_value, &_timestamp);
		_isConverted = true;
	}
	return _timestamp;
}

void CInformixDateTimeValue::update ()
{
	_isConverted = false;
}


/////////////////////////////////////////////////////////////////////////////
// CInformixIntervalValue

CInformixIntervalValue::CInformixIntervalValue (int2 qualifier, IFetchSubject *fetchSubject)
{
	_value.in_qual = qualifier;
	_isConverted = false;
	fetchSubject->attach(this);
}

int2 CInformixIntervalValue::getType ()
{
	return SQLINTERVAL;
}

int4 CInformixIntervalValue::getLength ()
{
	return _value.in_qual;
}

char * CInformixIntervalValue::getData ()
{
	return reinterpret_cast<char *>(&_value);
}

void CInformixIntervalValue::asString (char *value)
{
	if (!_isConverted) {
		interval_to_char(&_value, _buffer);
		_isConverted = true;
	}
	memcpy(value, _buffer, sizeof(_buffer));
}

void CInformixIntervalValue::update ()
{
	_isConverted = false;
}


/////////////////////////////////////////////////////////////////////////////
// CInformixDateValue

CInformixDateValue::CInformixDateValue ()
{
}

CInformixDateValue::CInformixDateValue (INT32 value)
{
	_value = value - DATE_DELTA;
}

int2 CInformixDateValue::getType ()
{
	return SQLDATE;
}

int4 CInformixDateValue::getLength ()
{
	return sizeof(_value);
}

char * CInformixDateValue::getData ()
{
	return reinterpret_cast<char *>(&_value);
}

CInformixDateValue::operator INT32 ()
{
	return _value + DATE_DELTA;
}


/////////////////////////////////////////////////////////////////////////////
// CInformixAbstractInt8Value

CInformixAbstractInt8Value::CInformixAbstractInt8Value (IFetchSubject *fetchSubject)
{
	_hasDouble = false;
	_hasBcd = false;
	fetchSubject->attach(this);
}

int4 CInformixAbstractInt8Value::getLength ()
{
	return sizeof(_value);
}

char * CInformixAbstractInt8Value::getData ()
{
	return reinterpret_cast<char *>(&_value);
}

double CInformixAbstractInt8Value::asDouble ()
{
	if (!_hasDouble) {
		_double = int8_to_double(&_value);
		_hasDouble = true;
	}
	return _double;
}

FMTBcd& CInformixAbstractInt8Value::asFMTBcd ()
{
	if (!_hasBcd) {
		int8_to_bcd(&_value, &_bcd);
		_hasBcd = true;
	}
	return _bcd;
}

void CInformixAbstractInt8Value::update ()
{
	_hasDouble = false;
	_hasBcd = false;
}


/////////////////////////////////////////////////////////////////////////////
// CInformixAbstractSimpleLOValue

CInformixAbstractSimpleLOValue::CInformixAbstractSimpleLOValue (int4 size)
{
	memset(&_value, 0, sizeof(_value));

	_value.loc_loctype = LOCMEMORY;

	if ((_value.loc_bufsize = size) < 0)
		_value.loc_mflags = LOC_ALLOC;
	else
		_value.loc_buffer = new char [size];
}

CInformixAbstractSimpleLOValue::CInformixAbstractSimpleLOValue (int2 sqltype, int4 size, void *value)
{
	memset(&_value, 0, sizeof(_value));

	_value.loc_type = sqltype;

	_value.loc_loctype = LOCMEMORY;
	_value.loc_bufsize = size;
	_value.loc_size = size;
	_value.loc_buffer = new char [size];

	memcpy(_value.loc_buffer, value, size);
}

CInformixAbstractSimpleLOValue::~CInformixAbstractSimpleLOValue ()
{
	if (_value.loc_mflags == LOC_ALLOC)
		freeSimpleLOBuffer(_value.loc_buffer);
	else
		delete [] _value.loc_buffer;
}

int4 CInformixAbstractSimpleLOValue::getLength ()
{
	return sizeof(_value);
}

char * CInformixAbstractSimpleLOValue::getData ()
{
	return reinterpret_cast<char *>(&_value);
}

int4 CInformixAbstractSimpleLOValue::getBlobSize ()
{
	return _value.loc_size;
}

void CInformixAbstractSimpleLOValue::getBlobData (int4 length, void *value)
{
	memcpy(value, _value.loc_buffer, length);
}


/////////////////////////////////////////////////////////////////////////////
// CInformixAbstractOpaqueValue

CInformixAbstractOpaqueValue::CInformixAbstractOpaqueValue ()
{
	checkConnect();
	_value = 0;
	checkInformixResult(ifx_var_flag(&_value, 1));
	checkInformixResult(ifx_var_init(&_value));
}

CInformixAbstractOpaqueValue::CInformixAbstractOpaqueValue (int4 size, void *value)
{
	checkConnect();
	_value = 0;
	checkInformixResult(ifx_var_flag(&_value, 0));
	checkInformixResult(ifx_var_alloc(&_value, size + 1));
	checkInformixResult(ifx_var_setlen(&_value, size));
	checkInformixResult(ifx_var_setdata(&_value, reinterpret_cast<char *>(value), size));
	checkInformixResult(ifx_var_init(&_value));
}

void CInformixAbstractOpaqueValue::checkConnect ()
{
#ifdef WIN32
	if (g_ifx_var_freevar == 0)
		throw CInformixDriverError("Used data type requires Informix 2.80 or later connectivity software");
#endif
}

CInformixAbstractOpaqueValue::~CInformixAbstractOpaqueValue ()
{
#ifdef WIN32
	g_ifx_var_freevar(&_value);
#else
	ifx_var_freevar(&_value);
#endif
}

int4 CInformixAbstractOpaqueValue::getLength ()
{
	return 0;
}

char * CInformixAbstractOpaqueValue::getData ()
{
	return reinterpret_cast<char *>(_value);
}

void CInformixAbstractOpaqueValue::asString (char *value)
{
	getBlobData(getBlobSize(), value);
}

int4 CInformixAbstractOpaqueValue::getBlobSize ()
{
	return ifx_var_getlen(&_value) - 1;
}

void CInformixAbstractOpaqueValue::getBlobData (int4 length, void *value)
{
	if (length > 1)
		memcpy(value, ifx_var_getdata(&_value), length);
	else
		*reinterpret_cast<char *>(value) = 0;
}


/////////////////////////////////////////////////////////////////////////////
// CInformixAbstractSmartLOValue

CInformixAbstractSmartLOValue::CInformixAbstractSmartLOValue (IFetchSubject *fetchSubject)
{
	_isOpened = false;
	_isMeasured = false;
	fetchSubject->attach(this);
}

CInformixAbstractSmartLOValue::CInformixAbstractSmartLOValue (int4 size, void *value)
{
	_isOpened = false;

	mint error;
	ifx_int8 estbytes;
	ifx_lo_create_spec_t *create_spec = 0;
	mint byteswrote;

	checkInformixResult(ifx_lo_def_create_spec(&create_spec));

	ifx_lo_specset_flags(create_spec, LO_LOG);
	ifx_int8cvint(size, &estbytes);
	ifx_lo_specset_estbytes(create_spec, &estbytes);
	if ((_descriptor = ifx_lo_create(create_spec, LO_RDWR, &_value, &error)) == -1)
		checkInformixResult(error);
	_isOpened = true;

	byteswrote = ifx_lo_write(_descriptor, reinterpret_cast<char *>(value), size, &error);
	if (byteswrote < size)
		checkInformixResult(error);
}

CInformixAbstractSmartLOValue::~CInformixAbstractSmartLOValue ()
{
	if (_isOpened)
		ifx_lo_close(_descriptor);
}

int2 CInformixAbstractSmartLOValue::getType ()
{
	return SQLUDTFIXED;
}

int4 CInformixAbstractSmartLOValue::getLength ()
{
	return sizeof(_value);
}

char * CInformixAbstractSmartLOValue::getData ()
{
	return reinterpret_cast<char *>(&_value);
}

int4 CInformixAbstractSmartLOValue::getBlobSize ()
{
	if (!_isMeasured) {
		ifx_lo_stat_t *lo_stat;
		checkInformixResult(ifx_lo_stat(getDescriptor(), &lo_stat));
		ifx_int8_t size;
		ifx_lo_stat_size(lo_stat, &size);
		ifx_int8toint(&size, reinterpret_cast<mint *>(&_size));
		_isMeasured = true;
		ifx_lo_stat_free(lo_stat);
	}
	return _size;
}

void CInformixAbstractSmartLOValue::getBlobData (int4 length, void *value)
{
	mint error;
	mint bytesread;
	bytesread = ifx_lo_read(getDescriptor(), static_cast<char *>(value), length, &error);
	if (bytesread < length)
		checkInformixResult(error);
}

mint CInformixAbstractSmartLOValue::getDescriptor ()
{
	if (!_isOpened) {
		mint error;
		if ((_descriptor = ifx_lo_open(&_value, LO_RDONLY, &error)) == -1)
			checkInformixResult(error);
		_isOpened = true;
	}
	return _descriptor;
}

void CInformixAbstractSmartLOValue::update ()
{
	if (_isOpened) {
		checkInformixResult(ifx_lo_close(_descriptor));
		_isOpened = false;
		_isMeasured = false;
	}
}


/////////////////////////////////////////////////////////////////////////////
// CInformixVariable

CInformixVariable::CInformixVariable ()
{
	_value = 0;
	_isNull = -1;
	_sqlvar = 0;
}

CInformixVariable::~CInformixVariable ()
{
	delete _value;
}

void CInformixVariable::initialize (ifx_sqlvar_t *sqlvar)
{
	_sqlvar = sqlvar;
	_sqlvar->sqlind = &_isNull;
}

int2 CInformixVariable::getType () const
{
	return _sqlvar->sqltype;
}

int4 CInformixVariable::getLength () const
{
	return _sqlvar->sqllen;
}

CInformixAbstractValue * CInformixVariable::getValue () const
{
	return _value;
}

void CInformixVariable::setValue (CInformixAbstractValue *value)
{
	delete _value;
	_value = value;
	_sqlvar->sqltype = value->getType();
	_sqlvar->sqllen = value->getLength();
	_sqlvar->sqldata = value->getData();
	_sqlvar->sqlxid = value->getXID();
	_sqlvar->sqltypename = value->getTypeName();
	_sqlvar->sqltypelen = value->getTypeNameLength();
	_isNull = 0;
}

bool CInformixVariable::isNull () const
{
	return _isNull != 0;
}

void CInformixVariable::setNull ()
{
	delete _value;
	_value = 0;
	_isNull = -1;
}

char * CInformixVariable::getName () const
{
	return _sqlvar->sqlname;
}

int4 CInformixVariable::getXID () const
{
	return _sqlvar->sqlxid;
}

char * CInformixVariable::getTypeName () const
{
	if (_sqlvar->sqltypename == 0)
		_sqlvar->sqltypename = rtypname(getType() & SQLTYPE);
	return _sqlvar->sqltypename;
}
